# coding: utf-8
# author: t_wolf

import os
import re
import sys
import stat
import math
import time
import copy
import types
import base64
import pickle
import shelve
import random
import hashlib
import sqlite3
import calendar
import StringIO
from datetime import datetime
from threading import Thread, Timer
from multiprocessing import Process, Manager, Value, Array, Pipe
import socket

import framework.dependence.redis as redis
from framework.dependence import demjson

reload(sys)
sys.setdefaultencoding("UTF-8")


class MyImportLib:
    def __init__(self):
        pass

    @staticmethod
    def import_module(module_path):
        return __import__(module_path, {}, {}, [''])


try:
    import importlib
except ImportError:
    importlib = MyImportLib

# function call
CURR_FUNC_POSITION = 0
CURR_FILE_POSITION = 1
CALL_FILE_POSITION = 2

get_frame = sys._getframe
ROOT_PATH = os.path.abspath(os.curdir)
PLATFORM = sys.platform


def run_jar():
    pass


def write_console(out_str, pipe=sys.stdout):
    pipe.write("%s%s" % (out_str, os.linesep))


class FormatWriter(object):
    def __init__(self, format_type='table', in_separator=' ', out_separator=' ', fell=4, header=''):
        self.list = []
        self.type = format_type.lower()
        self.ctor = (in_separator, out_separator)
        self.fell = fell
        self.head = header
        self.max_size = 0
        self.row = 0
        self.col = 0

    def eat(self, format_str):
        format_list = [s.strip() for s in format_str.split(self.ctor[0])]

        if not self.col:
            self.col = len(format_list)

        if self.col != len(format_list):
            raise ValueError("Wrong Arguments of this input string [%s]." % format_str)

        self.max_size = reduce(lambda x, y: x if x >= y else y, [len(l) for l in format_list])
        self.list.append(format_list)
        self.row += 1

        return None

    def add_line(self):
        if self.row:
            self.list.append(["" for i in range(self.col)])

    def set_title(self, title_str):
        self.list.insert(0, ['[%s]' % t.strip() for t in title_str.split(self.ctor[0])])

    def __render(self):

        max_fell = self.max_size + self.fell

        for _row in self.list:
            sys.stdout.write(self.head)

            if isinstance(_row, str):
                sys.stdout.write(_row)

            elif isinstance(_row, list):
                for _col in _row:
                    sys.stdout.write("%s%s" % (_col, self.ctor[1] * (max_fell - len(_col))))

            sys.stdout.write(os.linesep)

    def spit(self):

        if self.type == 'default':
            pass

        return self.__render()


class SharePool(object):
    """Base class for session stores"""

    def __contains__(self, key):
        raise NotImplementedError

    def __getitem__(self, key):
        raise NotImplementedError

    def __setitem__(self, key, value):
        raise NotImplementedError

    def get(self, key, default):
        raise NotImplementedError

    def set(self, key, value, expired):
        raise NotImplementedError

    def delete(self, key):
        raise NotImplementedError

    def encode(self, share_dict):
        """encodes object dict as a string"""
        pickled = pickle.dumps(share_dict)
        return base64.encodestring(pickled)

    def decode(self, share_data):
        """decodes the data to get back the object dict """
        pickled = base64.decodestring(share_data)
        return pickle.loads(pickled)


class RedisSharePool(SharePool):
    def __init__(self, redis_connection, fresh_expired=False):
        self.fresh = fresh_expired
        self.conn = redis_connection

    def flush_timeout(self, key):
        expired = self.decode(self.conn.get(key))[0]
        if expired:
            return self.conn.expire(key, expired)

    def __contains__(self, key):
        return self.conn.exists(key)

    def __getitem__(self, key):
        if key in self:
            self.flush_timeout(key)
            return self.decode(self.conn.get(key))[1]
        else:
            raise KeyError

    def __setitem__(self, key, value):
        pickled = self.encode((None, value))
        return self.conn.set(key, pickled)

    def __delitem__(self, key):
        if key in self:
            self.conn.delete(key)

    def get(self, key, default=None):
        if key in self:
            self.flush_timeout(key)
            return self.decode(self.conn.get(key))[1]
        else:
            return default

    def set(self, key, value, expired=None):
        pickled = self.encode((expired, value))
        expired = {'ex': expired} if expired else {}
        return self.conn.set(key, pickled, **expired)

    def delete(self, key):
        if key in self:
            self.conn.delete(key)


class FileSharePool(SharePool):
    def __init__(self, root):
        # if the storage root doesn't exists, create it.
        if not os.path.exists(root):
            os.makedirs(
                os.path.abspath(root)
            )
        self.root = root

    def _get_path(self, key):
        if os.path.sep in key:
            raise ValueError("Bad key: %s" % repr(key))
        return os.path.join(self.root, key)

    def __contains__(self, key):
        path = self._get_path(key)
        return os.path.exists(path)

    def __getitem__(self, key):
        path = self._get_path(key)
        if os.path.exists(path):
            pickled = open(path).read()
            a, v, e = self.decode(pickled)
            self[key] = (v, e)
            return v
        else:
            raise KeyError(key)

    def __setitem__(self, key, value):
        path = self._get_path(key)
        pickled = self.encode((time.time(), value[0], value[1]))
        try:
            f = open(path, 'w')
            try:
                f.write(pickled)
            finally:
                f.close()
        except IOError:
            pass

    def __delitem__(self, key):
        path = self._get_path(key)
        if os.path.exists(path):
            os.remove(path)

    def del_expired(self, key):
        now = time.time()
        if key in self:
            path = self._get_path(key)
            pickled = open(path).read()
            a, v, e = self.decode(pickled)
            if e and now - a > e:
                del self[key]

    def get(self, key, default):
        self.del_expired(key)
        return self[key] if key in self else default

    def set(self, key, value, expired=None):
        self[key] = (value, expired)

    def delete(self, key):
        del self[key]


class ShelfSharePool(SharePool):
    """no through processes, only can through threads."""

    def __init__(self, shelf):
        self.shelf = shelf

    def __contains__(self, key):
        return key in self.shelf

    def __getitem__(self, key):
        a, v, e = self.shelf[key]
        self[key] = (v, e)
        return v

    def __setitem__(self, key, value):
        self.shelf[key] = time.time(), value[0], value[1]

    def __delitem__(self, key):
        try:
            del self.shelf[key]
        except KeyError:
            pass

    def del_expired(self, key):
        now = time.time()
        if key in self.shelf:
            a, v, e = self.shelf[key]
            if e and now - a > e:
                del self[key]

    def get(self, key, default=None):
        self.del_expired(key)
        return self[key] if key in self.shelf else default

    def set(self, key, value, expired=None):
        self[key] = (value, expired)

    def delete(self, key):
        del self[key]


class DBSharePool(SharePool):
    def __init__(self, db, table_name):
        self.db = db
        self.table = table_name
        self.db.query(
            "CREATE TABLE IF NOT EXISTS %s ("
            "key_name CHAR(128) UNIQUE NOT NULL,"
            "atime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,"
            "data text"
            ")" % self.table
        )

    def __contains__(self, key):
        data = self.db.select(self.table, where="key_name=$key", vars=locals())
        return bool(list(data))

    def __getitem__(self, key):
        now = datetime.now()
        try:
            s = self.db.select(self.table, where="key_name=$key", vars=locals())[0]
            self.db.update(self.table, where="key_name=$key", atime=now, vars=locals())
        except IndexError:
            raise KeyError
        else:
            v, e = self.decode(s.data)
            return v

    def __setitem__(self, key, value):
        pickled = self.encode(value)
        now = datetime.now()
        if key in self:
            self.db.update(self.table, where="key_name=$key", data=pickled, vars=locals())
        else:
            self.db.insert(self.table, False, key_name=key, data=pickled)

    def __delitem__(self, key):
        self.db.delete(self.table, where="key_name=$key", vars=locals())

    def del_expired(self, key):
        now = time.time()
        if key in self:
            s = self.db.select(self.table, where="key_name=$key", vars=locals())[0]
            v, e = self.decode(s.data)
            if e and now - datetime2timestamp(s.atime) > e:
                del self[key]

    def get(self, key, default=None):
        self.del_expired(key)
        return self[key] if key in self else default

    def set(self, key, value, expired=None):
        self[key] = (value, expired)

    def delete(self, key):
        del self[key]


def get_abs_path(position):
    return os.path.split(os.path.abspath(get_frame(position).f_code.co_filename))[0]


def get_current_time(time_format='%y-%m-%d %H:%M:%S'):
    """
    返回格式化后的当前时间
    :param time_format: 格式化格式
    :return: 返回string类型的格式化后时间
    """
    return datetime.now().strftime(time_format)


def get_structure_time(time_str=time.time()):
    """
    获取规格化时间
    :param time_str: 时间戳
    :return: 一个时间列表
    """
    st_time = time.localtime(time_str)
    return [st_time.tm_min, st_time.tm_hour, st_time.tm_mday, st_time.tm_mon, st_time.tm_wday]


def time_to_delay(_future):
    delay_time = time.mktime(_future) - time.mktime(time.localtime())
    return delay_time if delay_time > 0 else 0


def get_weekday_by_ymd(_y, _m, _d):
    return datetime(_y, _m, _d).isocalendar()[2]


def get_current_month_days(_y, _m):
    return calendar.monthrange(_y, _m)[1]


def datetime2timestamp(date_obj):
    """
    将datetime类型转换为timestamp
    :param date_obj: datetime类型的数据
    :return: string类型的timestamp
    """
    date_stamp = time.strptime(date_obj, "%Y-%m-%d %H:%M:%S") if not isinstance(date_obj, datetime) else date_obj
    return time.mktime(date_stamp.timetuple())


def inject_object_by_dict(obj, obj_dict):
    """
    将一个字典的键值对注入为对象的属性
    :param obj: 准备注入的目标对象
    :param obj_dict: 要注入的字典
    :return: 返回被注入后的对象
    """
    for _key in obj_dict.keys():
        setattr(obj, _key, obj_dict[_key])
    return obj


def iter_factor(iter_obj):
    """
    一个数值序列
    :param iter_obj: 要迭代的数值上限
    :return: 返回一个迭代器
    """
    if isinstance(iter_obj, int):
        iter_obj = xrange(iter_obj)
    for _val in iter_obj:
        yield _val


def function_recursion_before(func_list, tag_func, asc=False):
    if asc:
        func_list.reverse()

    if len(func_list) == 1:

        return lambda: tag_func() if func_list[0]() else None

    else:

        return lambda: function_recursion_before(func_list[:-1], tag_func)() if func_list[-1]() else None


def function_recursion_after(func_list, tag_func, asc=False):
    if asc:
        func_list.reverse()

    if len(func_list) == 1:

        return lambda: func_list[0](tag_func())

    else:

        return lambda: func_list[-1](function_recursion_after(func_list[:-1], tag_func)())


def list_split_by_sign(args_list, sign):
    """
    将字符串数组以sign分割为字符串
    :param args_list: 字符串数组
    :param sign: 分割符号
    :return: 返回一个点调用结构
    """
    return reduce(
        lambda i, j: '%s%s%s' % (i, sign, j),
        args_list
    ) if len(args_list) else ""


def get_recurse_value(recurse_dict, key_list, default=lambda: None):
    """
    与list_to_dot_caller配置使用,主要用于解析命令字典
    :param recurse_dict: 命令字典
    :param key_list: list_to_dot_caller生成的.调用结构
    :param default: 如果调用的key不存在的默认处理
    :return: 返回callable值
    """
    arg_length = len(key_list)
    if arg_length >= 1:
        curr_step = recurse_dict.get(key_list[0], default)
        key_list.pop(0)
        # if isinstance(curr_step, dict) and arg_length > 1:
        #     return get_recurse_value(curr_step, key_list, default)
        # else:
        #     if callable(curr_step):
        #         if len(key_list):
        #             return curr_step(*key_list)
        #         else:
        #             return curr_step()
        #     else:
        #         return default()

        return get_recurse_value(
            curr_step, key_list, default
        ) if isinstance(curr_step, dict) and arg_length > 1 else (
            (
                curr_step(*key_list) if len(key_list) else curr_step()
            ) if callable(curr_step) else default()
        )

    else:

        return default()


dynamic_callee = get_recurse_value


class LOGGER:
    """
    日志对象
    """
    __LOG_LEVEL = {
        'DEV': -1,
        'INFO': 0,
        'DEBUG': 1,
        'WARNING': 2,
        'ERROR': 3,
        'CRITICAL': 4,
    }

    def __init__(self, level, out_file):
        self.__log_num = self.__LOG_LEVEL[level]
        self.outHandle = out_file

    def __write_log(func):
        def wrapper(self, _str):
            if self.__LOG_LEVEL[func.__name__.upper()] >= self.__log_num:
                p_file = get_frame(CURR_FILE_POSITION).f_code.co_filename.split("%s/" % ROOT_PATH)
                log_str = "[%s] <%s:%d> @ [%s]: %s" % (
                    get_current_time("%y-%m-%d %H:%M:%S-%f"),
                    p_file[1] if len(p_file) > 1 else p_file[0],
                    get_frame(CURR_FUNC_POSITION).f_back.f_lineno,
                    func.__name__.upper(),
                    _str
                )
                sys.stdout.write("%s%s" % (log_str, os.linesep))
                if PLATFORM in ('darwin', 'linux2'):
                    os.system("echo \"%s\" >> %s" % (log_str, self.outHandle))
                elif PLATFORM in ('win32',):
                    pass
                else:
                    pass
            func(self, _str)

        return wrapper

    @__write_log
    def dev(self, log_str):
        pass

    @__write_log
    def info(self, log_str):
        pass

    @__write_log
    def debug(self, log_str):
        pass

    @__write_log
    def error(self, log_str):
        pass

    @__write_log
    def warning(self, log_str):
        pass

    @__write_log
    def critical(self, log_str):
        sys.exit(1)


def json2params(str_obj):
    """
    将json格式转换为参数键值
    :param str_obj: 标准json格式
    :return: 键值对dict
    """
    return demjson.decode(str_obj, encoding="utf-8")


def params2json(str_obj, encoded=None):
    """
    将对象转换为json格式
    :param str_obj: 参数对象
    :param encoded: 是否转码
    :return: 返回一个标准json格式
    """
    return demjson.encode(str_obj, encoding=encoded)


class Storage(dict):
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError, k:
            raise AttributeError(k)

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, key):
        try:
            del self[key]
        except KeyError, k:
            raise AttributeError(k)

    def __repr__(self):
        return '<Storage ' + dict.__repr__(self) + '>'

    def get_value_from_dot_path(self, dot_path, _target=None):
        _target = self if not _target else _target
        try:
            _idx_dot = dot_path.index(".")
            _cur_sign = dot_path[:_idx_dot]
            _new_sign = dot_path[_idx_dot + 1:]

            if _cur_sign in _target:
                return _target.get_value_from_dot_path(_new_sign, _target[_cur_sign])
            else:
                raise RuntimeError

        except ValueError:
            if dot_path in _target:
                return _target[dot_path]
            else:
                raise RuntimeError


storage = Storage


def storager(dict_mapping, deep=999):
    """
        将dict转换成对象
        :param dict_mapping: 预处理对象
        :param deep: 转化深度
        :return: 返回转化后的对象
    """
    # 足够的深度,基本上全部转化
    tor = Storage()

    if deep == 0:
        return dict_mapping
    deep -= 1

    for _key, _value in dict_mapping.items():
        if isinstance(_value, dict):
            setattr(tor, _key, storager(_value, deep))
        else:
            setattr(tor, _key, _value)

    return tor


def xml2params(xml_obj):
    pass


def params2xml(xml_obj):
    pass


def platform_copy_directory(source_path, target_path):
    if PLATFORM in ('darwin', 'linux2'):
        system_command("cp -R %s %s" % (source_path, target_path))
    elif PLATFORM in ('win32',):
        if not os.path.exists(target_path):
            os.mkdir(target_path)
        system_command("xcopy /E /Q /Y %s %s" % (source_path, target_path))
    else:
        write_console("This Platform not Supported.")


def platform_remove_directory(target_path):
    if PLATFORM in ('darwin', 'linux2'):
        system_command("rm -rf %s" % target_path)
    elif PLATFORM in ('win32',):
        system_command("rmdir /S /Q %s" % target_path)
    else:
        write_console("This Platform not Supported.")


def platform_move(_source, _target):
    if PLATFORM in ('darwin', 'linux2'):
        system_command("mv %s %s" % (_source, _target))
    elif PLATFORM in ('win32',):
        pass
        # system_command("rmdir /S /Q %s" % target_path)
    else:
        write_console("This Platform not Supported.")


def replace_file_words(target_file, replace_pair):
    if os.path.isfile(target_file):
        origin_content = open(target_file).read()

        for _key, _value in replace_pair.items():
            origin_content = re.sub(_key, _value, origin_content)

        with open(target_file, 'wb') as _file:
            _file.write(origin_content)

    else:
        raise ValueError("[Error] %s is not a valid file." % target_file)


def search_in_content(search_pattern, target_content, index=0):
    try:
        res = re.search(search_pattern, target_content).group(index)
    except (AttributeError, ValueError):
        res = None

    return res


def search_in_file(search_pattern, target_file, index=0):
    return search_in_content(search_pattern, open(target_file).read(), index)


def empty(obj):
    if not isinstance(obj, type(None)):
        if isinstance(obj, (str, tuple, list, set)):
            if len(obj) > 0:
                return False
        if isinstance(obj, (dict, storage)):
            if len(obj.keys()) > 0:
                return False
        else:
            return False

    return True


not_empty = lambda o: not empty(o)


def import_module_from_file_path(file_path):
    if file_path.startswith(os.sep):
        file_path = file_path[1:]

    file_path = os.path.splitext(file_path)[0]
    return importlib.import_module(file_path.replace(os.sep, os.extsep))


def system_remove(_path):
    if os.path.exists(_path):
        if os.path.islink(_path):
            return os.remove(_path)
        elif os.path.isdir(_path):
            dir_files = os.listdir(_path)
            if len(dir_files):
                for _file in dir_files:
                    system_remove(os.path.join(_path, _file))

            return os.removedirs(_path)

        else:
            return os.remove(_path)


def system_remove_children(_path, _except=[], _expect=False):
    if os.path.exists(_path):
        if os.path.isdir(_path):
            for _dir in [x for x in os.listdir(_path) if (x in _except) ^ (not _expect)]:
                system_remove(os.path.join(_path, _dir))
            return True
        else:
            return False


def get_object_from_module(_module, instance):
    expect_dict = {}
    for _name in dir(_module):
        obj = getattr(_module, _name)
        if isinstance(obj, instance):
            expect_dict.update({_name: obj})

    return expect_dict


def system_command(cmd_str):
    return os.system(cmd_str)


def data_encode(origin_data):
    pickled = pickle.dumps(origin_data)
    return base64.encodestring(pickled)


def data_decode(encoded_data):
    pickled = base64.decodestring(encoded_data)
    return pickle.loads(pickled)


def decode_object(obj, code="UTF-8"):
    if isinstance(obj, dict):
        for _key, _value in obj.items():
            obj[_key] = decode_object(_value, code)
        return obj
    elif isinstance(obj, list):
        _target = []
        for _item in obj:
            _target.append(decode_object(_item, code))
        return _target
    elif isinstance(obj, tuple):
        _target = []
        for _item in obj:
            _target.append(decode_object(_item, code))
        return tuple(_target)
    elif isinstance(obj, set):
        _target = []
        for _item in obj:
            _target.append(decode_object(_item, code))
        return set(_target)
    elif isinstance(obj, str):
        return obj.decode(code)
    elif isinstance(obj, unicode):
        return obj
    else:
        return obj


def test_socket_connection(ip, port):
    tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    res = tcp.connect_ex((ip, int(port)))
    del tcp
    if not res:
        return 1
    else:
        return 0


def range2(x=None, y=None, _step=1):
    y = y + 1 if y else y
    return range(x, y, _step)


def number_sorted(_list, reserve=False):
    origin_list = _list
    expect_list = []
    for i in xrange(len(_list)):
        min_var = origin_list[0]
        min_step = 0
        for j in xrange(1, len(origin_list)):
            if origin_list[j] < min_var:
                min_var = origin_list[j]
                min_step = j
        expect_list.insert(0, min_var) if reserve else expect_list.append(min_var)
        del origin_list[min_step]

    return expect_list


def get_sorted_index_near(_list, _tag, after=True, reserve=False):
    _expect_list = list(_list)
    if _tag not in _list:
        _expect_list.append(_tag)

    return number_sorted(_expect_list, reserve).index(_tag) + (1 if after else -1)


def loop_generator(_iterable, **kwargs):
    initial = kwargs.get("initial", 0)
    near_by = kwargs.get("near_by", False)
    notice_end = kwargs.get("notice_end", False)
    try:
        _cursor = _iterable.index(initial)
    except ValueError:
        if len(_iterable) > 1 and near_by:
            _cursor = get_sorted_index_near(_iterable, initial)
        else:
            _cursor = 0
    _length = len(_iterable) if notice_end else len(_iterable) - 1

    while True:
        _last = _cursor
        if _cursor + 1 > _length:
            _cursor = 0
        else:
            _cursor += 1

        if notice_end and _cursor == 0:
            yield None
        else:
            yield _iterable[_last]


########################################################################################################################

if __name__ == '__main__':
    pass
